﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace Lexer {
	public class LexerPattern : List<CharPattern> {
		public const char REPEAT_CHAR = '+';
		public const char ESCAPE_CHAR = '\\';
		public const char NEGATE_CHAR = '!';

		public LexerPattern(params Object[] os) {
			bool negated = false;
			bool literal = false;
			foreach (object o in os) {
				if (o is IEnumerable<char>) {
					var s = (IEnumerable<char>)o;
					foreach (char c in s) {
						if (literal == true || nonMetaChar(c)) {
							this.Add(new CharPattern(new Regex(Regex.Escape(c.ToString())), negated));
							negated = false;
							literal = false;
						} else if (c == ESCAPE_CHAR) {
							literal = true;
						} else if (c == NEGATE_CHAR) {
							negated = true;
						} else if (c == REPEAT_CHAR) {
							if (this.Count == 0) {
								throw new InvalidOperationException("No previous element to apply " + REPEAT_CHAR + " to");
							}
							this.Last().IsRepeating = true;
						}
					}
				} else if (o is Regex) {
					if (literal) throw new ArgumentException("Cannot apply " + ESCAPE_CHAR + " to Regex element");
					this.Add(new CharPattern(o as Regex, negated));
					negated = false;
				} else {
					throw new ArgumentException("Can only support IEnumerable<char> or Regex elements, but received " + o.GetType().ToString());
				}
			}
			if (negated) {
				throw new InvalidOperationException("No next element to apply " + NEGATE_CHAR + " to");
			}
		}

		public static IEnumerable<char> MetaChars {
			get {
				yield return REPEAT_CHAR;
				yield return ESCAPE_CHAR;
				yield return NEGATE_CHAR;
			}
		}

		private bool nonMetaChar(char c) {
			return ! MetaChars.Contains(c);
		}

		public override string ToString() {
			StringBuilder result = new StringBuilder();
			foreach (CharPattern cp in this) {
				result.Append("/" + cp.ToString() + "/");
			}
			return result.ToString();
		}
	}
}
